package system

import (
	"fmt"
	"gin-admin/common/dto/response"
	"gin-admin/core"
	"gin-admin/enumeration"
	"gin-admin/global"
	"gin-admin/model"
	"gin-admin/service/dto/request"
	userRoleResponse "gin-admin/service/dto/response"
	"gin-admin/utils"

	"github.com/mitchellh/mapstructure"
	"gorm.io/gorm"
)

type UserRoleService struct{}

var UserRoleServiceApp = &UserRoleService{}

func (urs *UserRoleService) CreateUserRoleService(requestData *request.CreateOrModifyUserRoleRequest) *response.ServiceError {
	var message string
	err := core.UseTransactionWith(global.GA_DB, func(tx *gorm.DB) *response.ServiceError {
		var user model.User
		_, err := core.GenericQueryByUid(tx, requestData.UserID, &user, func() *response.ServiceError {
			message = response.GetStatusCode(response.UserNotFoundCode).Message
			return response.WrapError(response.UserNotFoundCode, message)
		})
		message = response.GetStatusCode(response.FailedCode).Message
		if err != nil {
			return response.WrapError(response.FailedCode, message)
		}
		var validRoles []model.Role
		tx.Where("id IN ?", requestData.Roles).Find(&validRoles)
		if len(validRoles) != len(requestData.Roles) {
			invalidRoles := getInvalidRoles(validRoles, requestData.Roles)
			message = fmt.Sprintf("无效的角色: %v", invalidRoles)
			return response.WrapError(response.RoleNotFoundCode, message)
		}
		for _, role := range validRoles {
			var existsUserRole model.UserRole
			result := tx.Where("user_id = ? AND role_id = ?", requestData.UserID, role.ID).First(&existsUserRole)
			if err := core.HandelError(result); err != nil {
				message = response.GetStatusCode(response.FailedCode).Message
				return response.WrapError(response.FailedCode, message)
			}
			if result.RowsAffected > 0 {
				continue
			}
			newUserRole := model.UserRole{
				UserID: user.UserID,
				RoleID: role.ID,
			}
			result = tx.Create(&newUserRole)
			if err := core.HandelError(result); err != nil {
				global.GA_LOGGER.Sugar().Errorw("批量插入用户角色数据失败", "error", result.Error)
				return response.WrapError(response.FailedCode, message)
			}
		}
		return nil
	})
	if err != nil {
		message = response.GetStatusCode(response.FailedCode).Message
		return response.WrapError(response.FailedCode, message)
	}
	return nil
}

func (urs *UserRoleService) DeleteUserRoleService(requestData *request.DetailUserRoleRequest) *response.ServiceError {
	var userRole model.UserRole
	var message string
	_, err := core.GenericQueryById(global.GA_DB, requestData.ID, &userRole, func() *response.ServiceError {
		message = response.GetStatusCode(response.UserRoleNotFoundCode).Message
		return response.WrapError(response.UserRoleNotFoundCode, message)
	})
	message = response.GetStatusCode(response.FailedCode).Message
	if err != nil {
		return response.WrapError(response.FailedCode, message)
	}
	if err := core.GenericLogicalDelete(global.GA_DB, &userRole); err != nil {
		global.GA_LOGGER.Sugar().Errorw("删除用户角色失败", "error", err.Error)
		return response.WrapError(response.FailedCode, message)
	}
	return nil
}

func (urs *UserRoleService) ModifyUserRoleService() (int64, error) {
	return 0, nil
}

func (urs *UserRoleService) GetUserRoleInfoService(requestData *request.UserRoleInfoRequest) (*userRoleResponse.UserRoleInfoResponse, *response.ServiceError) {
	var userRoleInfo userRoleResponse.UserRoleInfoResponse
	message := response.GetStatusCode(response.FailedCode).Message

	err := core.UseTransactionWith(global.GA_DB, func(tx *gorm.DB) *response.ServiceError {
		var queryResult map[string]interface{}
		result := tx.Table("sys_user_role AS ur").
			Select(`
				ur.*,
				u.uid AS uid, u.username as username, u.nickname AS nickname,
				r.id AS role_id, r.code AS role_code, r.name AS role_name`).
			Joins("LEFT JOIN sys_user AS u ON ur.user_id = u.uid").
			Joins("LEFT JOIN sys_role AS r ON ur.role_id = r.id").
			Where("ur.id = ?", requestData.ID).
			Where("ur.is_delete = ?", false).
			Order("ur.created_at ASC").
			Scan(&queryResult)
		if err := core.HandelError(result); err != nil {
			return response.WrapError(response.FailedCode, message)
		}
		if err := utils.ModelToStruct(&userRoleInfo, &queryResult); err != nil {
			return err
		}
		if err := mapstructure.Decode(queryResult, &userRoleInfo); err != nil {
			return response.WrapError(response.FailedCode, message)
		}
		userRoleInfo.StatusName = enumeration.CommonStatus(queryResult["status"].(int64)).StatusString()
		return nil
	})
	if err != nil {
		return &userRoleInfo, err
	}
	return &userRoleInfo, nil
}

func (urs *UserRoleService) GetUserRoleListService(requestData *request.UserRoleListRequest) (*response.PaginationResponse, int64, error) {
	var userRoleList response.PaginationResponse
	page := requestData.Page
	if page <= 0 {
		page = 1
	}
	size := requestData.Size
	if size <= 0 {
		size = 10
	}

	offset := (page - 1) * size
	var userRoleInfos []userRoleResponse.UserRoleInfoResponse
	query := global.GA_DB.Table("sys_user_role as ur").
		Select(`
		ur.id AS id, ur.status AS status, ur.created_at AS created_at, ur.updated_at AS updated_at, ur.description AS description,
		u.user_id AS uid, u.username as username, u.nickname AS nickname,
		r.id AS role_id, r.code AS role_code, r.name AS role_name
	`).
		Joins("LEFT JOIN sys_user AS u ON ur.user_id = u.user_id").
		Joins("LEFT JOIN sys_role AS r ON ur.role_id = r.id").
		Where("ur.is_delete = ?", false).
		Limit(size).
		Offset(offset)

	result := query.Scan(&userRoleInfos)
	if err := core.HandelError(result); err != nil {
		return nil, response.FailedCode, err
	}
	var total int64
	result = global.GA_DB.Model(&model.UserRole{}).Count(&total)
	if err := core.HandelError(result); err != nil {
		return nil, response.FailedCode, err
	}

	for _, info := range userRoleInfos {
		info.StatusName = enumeration.CommonStatus(info.Status).StatusString()
		userRoleList.List = append(userRoleList.List, info)
	}

	userRoleList.Page = page
	userRoleList.Size = size
	userRoleList.Total = total

	return &userRoleList, response.SuccessCode, nil
}

func getInvalidRoles(validRoles []model.Role, insertRoles []string) []string {
	validRoleMap := make(map[string]bool)
	for _, role := range validRoles {
		validRoleMap[role.ID] = true
	}
	var invalidRoles []string
	for _, roleId := range insertRoles {
		if !validRoleMap[roleId] {
			invalidRoles = append(invalidRoles, roleId)
		}
	}
	return invalidRoles
}
